#ifndef UTIL_H_
#define UTIL_H_

#include "graphics/graphics.h"
#include "graphics/surface.h"
#include "draw/SDL_draw.h"
#include "SDL/SDL_ttf.h"
#include <iostream>

using namespace std;

#define FLIP_VERTICAL		1
#define FLIP_HORIZONTAL		2

class Util
{
public:
	/**
	* Removes white spaces in a line.
	* 
	* @param str The string
	**/
	static string RemoveWhiteSpaces(string str)
	{
		string::iterator iter = str.begin();
		
		while(iter != str.end())
		{
			if( *iter == ' ' )
			{
				if( iter + 1 == str.end() )
				{
					str.resize( str.length() - 1 );
				}
				else
				{
					str.replace( iter, iter + 1, "" );
				}
			}
			
			if( iter != str.end() )
				iter++;
		}
		
		return str;
	}
	
	/**
	* Replaces a string with another string.
	* 
	* @param source The source string
	* @param old The string to be replaced
	* @param replace The replacement string
	* @return The new string
	**/
	static string Replace(string source, string old, string replace)
	{
		if( replace.find(old) == string::npos )
		{
			int pos;
			while( (pos = source.find(old)) != (int)string::npos )
			{
				source.replace(pos,old.length(),replace);
			}
		}
		
		return source;
	}

	/**
	* Splits the string.
	* 
	* @param str The string to split
	* @param c The character to split on
	* @return An array of strings
	**/
	static vector<string> Split(string str, char c)
	{
		vector<string> split;
		
		string::size_type loc;
		while( (loc = str.find( c, 0 )) != string::npos )
		{
			if( loc == 0 )
			{
				str.replace( str.begin(), str.begin() + 1, "" );
			}
			else
			{
				split.push_back(str.substr(0, loc));
				str.replace( str.begin(), str.begin() + loc + 1, "" );
			}
		}
		
		split.push_back(str);
		
		return split;
	}
	
	/**
	* Formats the string, splitting it into lines.
	*
	* @param str The string to format
	* @param font The font to use
	* @param size The size of the font
	* @param width The maximum width
	* @param textWidth The actual width
	* @param textHeight The actual height
	* @return The lines
	**/
	static vector<string> Format(string str, Font font, FontSize size, int width, int* textWidth, int* textHeight)
	{
		vector<string> lines;
		*textWidth = *textHeight = 0;
		int tokenPos = 0;
		int strWidth = 0, strHeight = 0;
		vector<string> tokens = Util::Split(str, ' ');
		while( tokenPos < (int)tokens.size() )
		{
			string line;
			bool fallThrough = false;
			do
			{
				if( !line.empty() )
					line += " ";
				
				// If \n is in the token, split the token and fall through
				if( tokens[tokenPos].find_first_of( '\n' ) != string::npos )
				{
					string second = tokens[tokenPos].substr(tokens[tokenPos].find_first_of( '\n' ) + 1, tokens[tokenPos].length());
					tokens.insert(tokens.begin() + tokenPos + 1, second);
					tokens[tokenPos] = tokens[tokenPos].substr(0, tokens[tokenPos].find_first_of( '\n' ));
					
					fallThrough = true;
				}
				
				line += tokens[tokenPos];
				tokenPos++;
				
				strWidth = width + 1;
				if( !fallThrough && tokenPos < (int)tokens.size() )
				{
					string temp = line + " " + tokens[tokenPos];
					
					TTF_Font* ttf = Graphics::GetGraphics()->GetFont(font,size);
					TTF_SizeText(ttf, temp.c_str(), &strWidth, &strHeight);
				}
			}
			while(!fallThrough && strWidth <= width);
			lines.push_back(line);
			TTF_Font* ttf = Graphics::GetGraphics()->GetFont(font,size);
			TTF_SizeText(ttf, line.c_str(), &strWidth, &strHeight);
			if( *textWidth < strWidth ) *textWidth = strWidth;
			*textHeight += strHeight + 1;
		}
		
		return lines;
	}
	
	/**
	* Flips a surface horizontally and/or vertically.
	* 
	* @param source The surface to flip
	* @param flags Flags for flip type
	* @return The flipped image
	**/
	static Surface* Flip(Surface* source, int flags)
	{
		SDL_Surface* surface = source->GetSurface();
		
	    //Pointer to the soon to be flipped surface
	    SDL_Surface *flipped = NULL;
	    
	    //If the image is color keyed
	    if( surface->flags & SDL_SRCCOLORKEY )
	    {
	        flipped = SDL_CreateRGBSurface( SDL_SWSURFACE, surface->w, surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, 0 );
	    }
	    //Otherwise
	    else
	    {
	        flipped = SDL_CreateRGBSurface( SDL_SWSURFACE, surface->w, surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask );
	    }
	    
	    //If the surface must be locked
	    if( SDL_MUSTLOCK( surface ) )
	    {
	        //Lock the surface
	        SDL_LockSurface( surface );
	    }
	    
	    //Go through columns
	    for( int x = 0, rx = flipped->w - 1; x < flipped->w; x++, rx-- )
	    {
	        //Go through rows
	        for( int y = 0, ry = flipped->h - 1; y < flipped->h; y++, ry-- )
	        {
	            //Get pixel
	            Uint32 pixel = GetPixel32( surface, x, y );
	            
	            //Copy pixel
	            if( ( flags & FLIP_VERTICAL ) && ( flags & FLIP_HORIZONTAL ) )
	            {
	                PutPixel32( flipped, rx, ry, pixel );
	            }
	            else if( flags & FLIP_HORIZONTAL )
	            {
	            	PutPixel32( flipped, rx, y, pixel );
	            }
	            else if( flags & FLIP_VERTICAL )
	            {
	            	PutPixel32( flipped, x, ry, pixel );
	            }
	        }    
	    }
	    
	    //Unlock surface
	    if( SDL_MUSTLOCK( surface ) )
	    {
	        SDL_UnlockSurface( surface );
	    }
	    
	    //Copy color key
	    if( surface->flags & SDL_SRCCOLORKEY )
	    {
	        SDL_SetColorKey( flipped, SDL_RLEACCEL | SDL_SRCCOLORKEY, surface->format->colorkey );
	    }
	    
	    //Return flipped surface
	    return new Surface(flipped);
	}
	
	/**
	* Replaces a color of a source surface with another color.
	* 
	* @param source The surface
	* @param from The color to replace
	* @param to The color to replace with
	* @return The resulting surface
	**/
	static Surface* ReplaceColor(Surface* source, Color from, Color to)
	{
		SDL_Surface* surface = source->GetSurface();
		
	    //Pointer to the destination
	    SDL_Surface *dest = NULL;
	    
	    //If the image is color keyed
	    if( surface->flags & SDL_SRCCOLORKEY )
	    {
	    	dest = SDL_CreateRGBSurface( SDL_SWSURFACE, surface->w, surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, 0 );
	    }
	    //Otherwise
	    else
	    {
	    	dest = SDL_CreateRGBSurface( SDL_SWSURFACE, surface->w, surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask );
	    }
	    
	    //If the surface must be locked
	    if( SDL_MUSTLOCK( surface ) )
	    {
	        //Lock the surface
	        SDL_LockSurface( surface );
	    }
	    
	    Color color;
	    //Go through columns
	    for( int x = 0; x < dest->w; x++ )
	    {
	        //Go through rows
	        for( int y = 0; y < dest->h; y++ )
	        {
	            //Get pixel
	            Uint32 pixel = GetPixel32( surface, x, y );
	            SDL_GetRGB(pixel, surface->format, &(color.r), &(color.g), &color.b);
	            
	            if( color.ToUint32() == from.ToUint32() )
	            {
	            	Draw_Pixel( dest, x, y, to.ToUint32() );
	            }
	            else PutPixel32( dest, x, y, pixel );
	        }    
	    }
	    
	    //Unlock surface
	    if( SDL_MUSTLOCK( surface ) )
	    {
	        SDL_UnlockSurface( surface );
	    }
	    
	    //Copy color key
	    if( surface->flags & SDL_SRCCOLORKEY )
	    {
	        SDL_SetColorKey( dest, SDL_RLEACCEL | SDL_SRCCOLORKEY, surface->format->colorkey );
	    }
	    
	    //Return flipped surface
	    return new Surface(dest);
	}
	
private:
	static Uint32 GetPixel32( SDL_Surface *surface, int x, int y )
	{
	    //Convert the pixels to 32 bit
	    Uint32 *pixels = (Uint32 *)surface->pixels;
	    
	    //Get the requested pixel
	    return pixels[ ( y * surface->w ) + x ];
	}

	static void PutPixel32( SDL_Surface *surface, int x, int y, Uint32 pixel )
	{
	    //Convert the pixels to 32 bit
	    Uint32 *pixels = (Uint32 *)surface->pixels;
	    
	    //Set the pixel
	    pixels[ ( y * surface->w ) + x ] = pixel;
	}
};

#endif /*UTIL_H_*/
